package dnl.dtools.grm.ftp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;

import org.apache.commons.net.ftp.FTPFile;

import ajul.lang.Args;
import dnl.dtools.grm.Resource;

/**
 * Represents an entry on a remote file system that is accessed via SSH.
 * 
 * @author Daniel Or
 * 
 */
public class FtpEntry extends Resource {

	private FtpSession session;
	private FTPFile ftpFile;
	private FtpEntry parent;

	public FtpEntry(URL url, FtpSession session)
			throws URISyntaxException {
		this(url, session, null, null);
	}

	private FtpEntry(URL url, FtpSession session, FtpEntry parent, FTPFile ftpFile)
	throws URISyntaxException {
		super(url);
		Args.assertNotNull(session);
		this.session = session;
		if(ftpFile == null){
			this.ftpFile = resolve();
		}
		else {
			this.ftpFile = ftpFile;
		}
		this.parent = parent;
	}

	private FTPFile resolve() {
		FTPFile[] files;
		try {
			files = session.getFtpClient().listFiles(getParentFileName());
			for (FTPFile file : files) {
				if(file.getName().equals(getRemoteFilePath())){
					return file;
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	private String getParentFileName(){
		String path = getRemoteFilePath();
		// trim any slashes at the end
		if(path.charAt(path.length()-1) == '/'){
			path = path.substring(0, path.length()-2);
		}
		int ind = path.lastIndexOf('/');
		path = path.substring(0, ind);
		return path;
	}
	
	private String getRemoteFilePath(){
		return url.getFile();
	}
	

	public FtpSession getSshSession() {
		return session;
	}

	@Override
	public boolean delete() throws IOException {
		session.getFtpClient().deleteFile(getRemoteFilePath());
		return true;
	}

	@Override
	public boolean exists() {
		return true;
	}

	@Override
	public Resource getChild(String name) {
		URL newUrl;
		try {
			newUrl = new URL(this.getUrl().toString() + "/" + name);
			FTPFile[] listFiles = session.getFtpClient().listFiles(getRemoteFilePath());
			for (FTPFile file : listFiles) {
				if(file.getName().equals(name)){
					return new FtpEntry(newUrl, this.session, this, file);
				}
			}
			return null;
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (URISyntaxException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public InputStream getInputStream() throws IOException {
		return session.getFtpClient().retrieveFileStream(getRemoteFilePath());
	}
	
	@Override
	public long getLastModified() {
		return ftpFile.getTimestamp().getTimeInMillis();
	}

	@Override
	public OutputStream getOutputStream() throws IOException {
		return session.getFtpClient().storeFileStream(getRemoteFilePath());
	}

	@Override
	public Resource getParent() {
		if(parent == null && !isRoot()) {
			resolveParent();
		}
		return parent;
	}

	private void resolveParent() {
		String parentFileName = getParentFileName();
		URL parentUrl;
		try {
			parentUrl = new URL(url.getProtocol()+"://"+url.getHost()+url.getPort()+"/"+parentFileName);
			parent = new FtpEntry(parentUrl, this.session, null, null);
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
	}

	@Override
	public long getSize() {
		return ftpFile.getSize();
	}

	@Override
	public boolean isArchive() {
		return false;
	}

	@Override
	public boolean isArchiveEntry() {
		return false;
	}		
	
	@Override
	public boolean isDirectory() {
		return ftpFile.isDirectory();
	}

	@Override
	public boolean isHidden() {
		return false;
	}
	
	@Override
	public boolean isLocal() {
		return false;
	}

	@Override
	public Resource[] list() {
		FTPFile[] listFiles;
		try {
			listFiles = session.getFtpClient().listFiles(getRemoteFilePath());
			FtpEntry[] entries = new FtpEntry[listFiles.length];
			
			for (int i = 0; i < entries.length; i++) {
				entries[i] = new FtpEntry(getChildURL(listFiles[i].getName()), this.session, this, listFiles[i]);
			}
			return entries;
		} catch (IOException e1) {
			e1.printStackTrace();
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
		return null;
	}

	private URL getChildURL(String child){
		StringBuffer sb = new StringBuffer(this.url.toString());
		if(sb.charAt(sb.length()-1) != '/'){
			sb.append('/');
		}
		sb.append(child);
		try {
			return new URL(sb.toString());
		} catch (MalformedURLException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	@Override
	public boolean mkDir() throws IOException {
		return session.getFtpClient().makeDirectory(getRemoteFilePath());
	}

	@Override
	public boolean renameTo(Resource dest) throws IOException {
		// TODO: assert on same host
		FtpEntry destFtp = (FtpEntry) dest;
		return session.getFtpClient().rename(getRemoteFilePath(), destFtp.getRemoteFilePath());
	}
	
	public boolean isRoot(){
		return this.getRemoteFilePath().equals("/");
	}

	

	@Override
	public String toString() {
		return "FtpEntry{"+getRemoteFilePath()+"}";
	}
	
}
